"""Cookie encryption/decryption utility using Fernet symmetric encryption.

This module provides utilities for encrypting and decrypting sensitive data,
specifically designed for cookies and passwords, using the Fernet symmetric
encryption scheme from the cryptography library. It manages a persistent
encryption key and offers a singleton pattern for easy access to the crypto
instance.
"""

import base64
import os
from pathlib import Path

from cryptography.fernet import Fernet
from loguru import logger


class CookieCrypto:
    """Manages encryption and decryption of data using Fernet.

    This class handles the loading and creation of a persistent encryption key
    and provides methods to encrypt and decrypt string data.
    """

    def __init__(self, key_path=None):
        """Initializes the CookieCrypto manager.

        Args:
            key_path: Optional; The path to the encryption key file.
                      If None, it defaults to ~/.swiftshadow/crypto.key.
        """
        if key_path is None:
            key_path = Path.home() / ".swiftshadow" / "crypto.key"

        self.key_path = key_path
        self.key = self._load_or_create_key()
        self.cipher = Fernet(self.key)

    def _load_or_create_key(self):
        """Loads an existing encryption key or creates a new one if it doesn't exist.

        The key is stored in the path specified during initialization. If a new key
        is created, its file permissions are set to 0o600 (owner read/write only).

        Returns:
            The encryption key as bytes.
        """
        if self.key_path.exists():
            # 加载现有密钥
            with open(self.key_path, "rb") as f:
                key = f.read()
            logger.debug(f"已加载加密密钥: {self.key_path}")
            return key
        else:
            # 生成新密钥
            key = Fernet.generate_key()
            self.key_path.parent.mkdir(parents=True, exist_ok=True)

            # 保存密钥并设置权限
            with open(self.key_path, "wb") as f:
                f.write(key)

            # 设置文件权限为仅所有者可读写 (0600)
            os.chmod(self.key_path, 0o600)

            logger.info(f"已生成新的加密密钥: {self.key_path}")
            return key

    def encrypt(self, data):
        """Encrypts a given string data.

        Args:
            data: The plaintext string to encrypt.

        Returns:
            The base64 URL-safe encoded encrypted string.

        Raises:
            Exception: If encryption fails.
        """
        try:
            encrypted = self.cipher.encrypt(data.encode("utf-8"))
            return base64.urlsafe_b64encode(encrypted).decode("utf-8")
        except Exception as e:
            logger.error(f"加密失败: {e}")
            raise

    def decrypt(self, encrypted_data):
        """Decrypts a base64 URL-safe encoded encrypted string.

        Args:
            encrypted_data: The base64 URL-safe encoded encrypted string to decrypt.

        Returns:
            The original plaintext string.

        Raises:
            Exception: If decryption fails.
        """
        try:
            encrypted = base64.urlsafe_b64decode(encrypted_data.encode("utf-8"))
            decrypted = self.cipher.decrypt(encrypted)
            return decrypted.decode("utf-8")
        except Exception as e:
            logger.error(f"解密失败: {e}")
            raise


# 全局实例
_crypto_instance: CookieCrypto | None = None


def get_crypto():
    """Retrieves the global singleton instance of CookieCrypto.

    If the instance does not exist, it is created.

    Returns:
        The singleton CookieCrypto instance.
    """
    global _crypto_instance
    if _crypto_instance is None:
        _crypto_instance = CookieCrypto()
    return _crypto_instance


def encrypt_cookies(cookies):
    """Encrypts cookie data.

    Args:
        cookies: The cookie data, either as a string or a dictionary.

    Returns:
        The encrypted cookie data as a base64 URL-safe string.
    """
    crypto = get_crypto()

    if isinstance(cookies, dict):
        # 转换为字符串格式
        cookie_str = "; ".join(f"{k}={v}" for k, v in cookies.items())
    else:
        cookie_str = cookies

    return crypto.encrypt(cookie_str)


def decrypt_cookies(encrypted_cookies):
    """Decrypts encrypted cookie data.

    Args:
        encrypted_cookies: The encrypted cookie data as a base64 URL-safe string.

    Returns:
        The original plaintext cookie string.
    """
    crypto = get_crypto()
    return crypto.decrypt(encrypted_cookies)


def encrypt_password(password):
    """Encrypts a plaintext password.

    Args:
        password: The plaintext password string.

    Returns:
        The encrypted password string.
    """
    return encrypt_cookies(password)


def decrypt_password(encrypted_password):
    """Decrypts an encrypted password.

    Args:
        encrypted_password: The encrypted password string.

    Returns:
        The original plaintext password string.
    """
    return decrypt_cookies(encrypted_password)
